home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / BasicComboPopup.java < prev    next >
Text File  |  1998-06-30  |  27KB  |  774 lines

  1. /*
  2.  * @(#)BasicComboPopup.java    1.3 98/04/10
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20.  
  21. package com.sun.java.swing.plaf.basic;
  22.  
  23. import com.sun.java.swing.*;
  24. import com.sun.java.swing.event.*;
  25. import java.awt.*;
  26. import java.awt.event.*;
  27. import java.beans.PropertyChangeListener;
  28. import java.beans.PropertyChangeEvent;
  29. import java.io.Serializable;
  30.  
  31.  
  32. /**
  33.  * This is an implementation of the ComboPopup interface.  It is primarily for use by
  34.  * BasicComboBoxUI and its subclasses.  BasicComboPopup extends JPopupMenu because
  35.  * most combo boxes use a popup menu to display the list of possible selections.
  36.  * BasicComboBoxUI only requires a ComboPopup, so subclasses of BasicComboBoxUI aren't
  37.  * required to use this class.
  38.  *
  39.  * All event handling is handled by createxxxListener() methods and internal classes.
  40.  * You can change the behavior of this class by overriding the createxxxListener()
  41.  * methods and supplying your own event listeners or subclassing from the ones supplied
  42.  * in this class.
  43.  *
  44.  * Inner classes for handling events:
  45.  *    InvocationMouseListener
  46.  *    InvocationMouseMotionListener
  47.  *    InvocationKeyListener
  48.  *    ListSelListener
  49.  *    ListMouseListener
  50.  *    ListMouseMotionListener
  51.  *    ComboPropertyChangeListener
  52.  *    ComboItemListener
  53.  *
  54.  * <p>
  55.  * Warning: serialized objects of this class will not be compatible with
  56.  * future swing releases.  The current serialization support is appropriate 
  57.  * for short term storage or RMI between Swing1.0 applications.  It will
  58.  * not be possible to load serialized Swing1.0 objects with future releases
  59.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  60.  * baseline for the serialized form of Swing objects.
  61.  *
  62.  * @version 1.3 04/10/98
  63.  * @author Tom Santos
  64.  */
  65. public class BasicComboPopup extends JPopupMenu implements ComboPopup {
  66.     protected JComboBox                comboBox;
  67.     protected JList                    list;
  68.     protected JScrollPane              scroller;
  69.  
  70.     // If the value is adjusting, any changes to the list selection won't affect the model.
  71.     protected boolean                  valueIsAdjusting = false;
  72.  
  73.     // Listeners that are required by the ComboPopup interface
  74.     protected MouseMotionListener      mouseMotionListener;
  75.     protected MouseListener            mouseListener;
  76.     protected KeyListener              keyListener;
  77.  
  78.     // Listeners that are attached to the list
  79.     protected ListSelectionListener    listSelectionListener;
  80.     protected MouseListener            listMouseListener;
  81.     protected MouseMotionListener      listMouseMotionListener;
  82.  
  83.     // Listeners that are attached to the JComboBox
  84.     protected PropertyChangeListener   propertyChangeListener;
  85.     protected ItemListener             itemListener;
  86.  
  87.     protected Timer                    autoscrollTimer;
  88.     protected boolean                  hasEntered = false;
  89.     protected boolean                  isAutoScrolling = false;
  90.     protected int                      scrollDirection = SCROLL_UP;
  91.  
  92.     protected static final int         SCROLL_UP = 0;
  93.     protected static final int         SCROLL_DOWN = 1;
  94.  
  95.     //========================================
  96.     // begin ComboPopup method implementations
  97.     //
  98.  
  99.     /**
  100.      * Implementation of ComboPopup.show().
  101.      */
  102.     public void show() {
  103.         Dimension popupSize = comboBox.getSize();
  104.         popupSize.setSize( popupSize.width, getPopupHeightForRowCount( comboBox.getMaximumRowCount() ) );
  105.         scroller.setMaximumSize( popupSize );
  106.         scroller.setPreferredSize( popupSize );
  107.         scroller.setMinimumSize( popupSize );
  108.         Rectangle popupBounds = computePopupBounds( 0, comboBox.getBounds().height,
  109.                                                     popupSize.width, popupSize.height);
  110.         list.invalidate();
  111.         list.setSelectedIndex( comboBox.getSelectedIndex() );
  112.         list.ensureIndexIsVisible( list.getSelectedIndex() );
  113.  
  114.         setLightWeightPopupEnabled( comboBox.isLightWeightPopupEnabled() );
  115.  
  116.         show( comboBox, popupBounds.x, popupBounds.y );
  117.     }
  118.  
  119.     /**
  120.      * Implementation of ComboPopup.hide().
  121.      */
  122.     public void hide() {
  123.         setVisible( false );
  124.         comboBox.repaint();
  125.     }
  126.  
  127.     /**
  128.      * Implementation of ComboPopup.getMouseListener().
  129.      */
  130.     public MouseListener getMouseListener() {
  131.         return mouseListener;
  132.     }
  133.  
  134.     /**
  135.      * Implementation of ComboPopup.getMouseMotionListener().
  136.      */
  137.     public MouseMotionListener getMouseMotionListener() {
  138.         return mouseMotionListener;
  139.     }
  140.  
  141.     /**
  142.      * Implementation of ComboPopup.getKeyListener().
  143.      */
  144.     public KeyListener getKeyListener() {
  145.         return keyListener;
  146.     }
  147.  
  148.     /**
  149.      * Called when the UI is uninstalling.  Since this popup isn't in the component
  150.      * tree, it won't get it's uninstallUI() called.  It removes the listeners that
  151.      * were added in addComboBoxListeners().
  152.      */
  153.     public void uninstallingUI() {
  154.         comboBox.removePropertyChangeListener( propertyChangeListener );
  155.         comboBox.removeItemListener( itemListener );
  156.     } 
  157.  
  158.     //
  159.     // end ComboPopup method implementations
  160.     //======================================
  161.  
  162.  
  163.     //===================================================================
  164.     // begin Initialization routines
  165.     //
  166.     public BasicComboPopup( JComboBox combo ) {
  167.         super();
  168.         comboBox = combo;
  169.  
  170.         mouseListener = createMouseListener();
  171.         mouseMotionListener = createMouseMotionListener();
  172.         keyListener = createKeyListener();
  173.  
  174.         listSelectionListener = createListSelectionListener();
  175.         listMouseListener = createListMouseListener();
  176.         listMouseMotionListener = createListMouseMotionListener();
  177.  
  178.         propertyChangeListener = createPropertyChangeListener();
  179.         itemListener = createItemListener();
  180.  
  181.         list = createList();
  182.         configureList();
  183.         scroller = createScroller();
  184.         configureScroller();
  185.         configurePopup();
  186.         addComboBoxListeners();
  187.     }
  188.  
  189.     /**
  190.      * Creates the mouse listener that is returned by ComboPopup.getMouseListener().
  191.      * Returns an instance of BasicComboPopup$InvocationMouseListener.
  192.      */
  193.     protected MouseListener createMouseListener() {
  194.         return new InvocationMouseListener();
  195.     }
  196.  
  197.     /**
  198.      * Creates the mouse motion listener that is returned by
  199.      * ComboPopup.getMouseMotionListener().
  200.      * Returns an instance of BasicComboPopup$InvocationMouseMotionListener.
  201.      */
  202.     protected MouseMotionListener createMouseMotionListener() {
  203.         return new InvocationMouseMotionListener();
  204.     }
  205.  
  206.     /**
  207.      * Creates the key listener that is returned by ComboPopup.getKeyListener().
  208.      * Returns an instance of BasicComboPopup$InvocationKeyListener.
  209.      */
  210.     protected KeyListener createKeyListener() {
  211.         return new InvocationKeyListener();
  212.     }
  213.  
  214.     /**
  215.      * Creates a list selection listener that watches for selection changes in
  216.      * the popup's list.
  217.      * Returns an instance of BasicComboPopup$ListSelListener.
  218.      */
  219.     protected ListSelectionListener createListSelectionListener() {
  220.         return new ListSelListener();
  221.     }
  222.  
  223.     /**
  224.      * Creates a mouse listener that watches for mouse events in
  225.      * the popup's list.
  226.      * Returns an instance of BasicComboPopup$ListMouseListener.
  227.      */
  228.     protected MouseListener createListMouseListener() {
  229.         return new ListMouseListener();
  230.     }
  231.  
  232.     /**
  233.      * Creates a mouse motion listener that watches for mouse events in
  234.      * the popup's list.
  235.      * Returns an instance of BasicComboPopup$ListMouseMotionListener.
  236.      */
  237.     protected MouseMotionListener createListMouseMotionListener() {
  238.         return new ListMouseMotionListener();
  239.     }
  240.  
  241.     /**
  242.      * Creates a property change listener that watches for changes in the bound
  243.      * properties in the JComboBox.
  244.      * Returns an instance of BasicComboPopup$ComboPropertyChangeListener.
  245.      */
  246.     protected PropertyChangeListener createPropertyChangeListener() {
  247.         return new ComboPropertyChangeListener();
  248.     }
  249.  
  250.     /**
  251.      * Creates an item listener that watches for changes in the selected
  252.      * item in the JComboBox.
  253.      * Returns an instance of BasicComboPopup$ComboItemListener.
  254.      */
  255.     protected ItemListener createItemListener() {
  256.         return new ComboItemListener();
  257.     }
  258.  
  259.     /**
  260.      * Creates the JList that is used in the popup to display the items in the model.
  261.      */
  262.     protected JList createList() {
  263.         return new JList( comboBox.getModel() );
  264.     }
  265.  
  266.     /**
  267.      * Called to configure the list created by createList().
  268.      */
  269.     protected void configureList() {
  270.         list.setFont( comboBox.getFont() );
  271.         list.setForeground( comboBox.getForeground() );
  272.         list.setBackground( comboBox.getBackground() );
  273.         list.setSelectionForeground( UIManager.getColor( "ComboBox.selectedForeground" ) );
  274.         list.setSelectionBackground( UIManager.getColor( "ComboBox.selectedBackground" ) );
  275.         list.setBorder( null );
  276.         list.setCellRenderer( comboBox.getRenderer() );
  277.         list.setRequestFocusEnabled( false );
  278.         addListListeners();
  279.     }
  280.  
  281.     /**
  282.      * Called by configureList() to add the necessary listeners to the list.
  283.      */
  284.     protected void addListListeners() {
  285.         list.addListSelectionListener( listSelectionListener ); 
  286.         list.addMouseMotionListener( listMouseMotionListener );
  287.         list.addMouseListener( listMouseListener );
  288.     }
  289.  
  290.     /**
  291.      * Creates the JScrollPane that is used in the popup to hold the list.
  292.      */
  293.     protected JScrollPane createScroller() {
  294.         return new JScrollPane( list, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
  295.                                 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER );
  296.     }
  297.  
  298.     /**
  299.      * Called to configure the JScrollPane created by createScroller().
  300.      */
  301.     protected void configureScroller() {
  302.         scroller.setRequestFocusEnabled( false );
  303.         scroller.getVerticalScrollBar().setRequestFocusEnabled( false );
  304.         scroller.setBorder( null );
  305.     }
  306.  
  307.     /**
  308.      * Called to configure this JPopupMenu (BasicComboPopup is a JPopupMenu).
  309.      */
  310.     protected void configurePopup() {
  311.         setLayout( new BoxLayout( this, BoxLayout.Y_AXIS ) );
  312.         setBorderPainted( true );
  313.         setBorder( BorderFactory.createLineBorder( Color.black ) );
  314.         setOpaque( false );
  315.         add( scroller );
  316.         setDoubleBuffered( true );
  317.         setRequestFocusEnabled( false );
  318.     }
  319.  
  320.     /**
  321.      * This method adds the necessary listeners to the JComboBox.
  322.      */
  323.     protected void addComboBoxListeners() {
  324.         comboBox.addPropertyChangeListener( propertyChangeListener );
  325.         comboBox.addItemListener( itemListener );
  326.     }
  327.  
  328.     //
  329.     // end Initialization routines
  330.     //=================================================================
  331.  
  332.  
  333.     //===================================================================
  334.     // begin Event Listenters
  335.     //
  336.  
  337.     /**
  338.      * This listener knows how and when to invoke this popup menu.  It also helps
  339.      * with click-and-drag scenarios by setting the selection if the mouse was
  340.      * released over the list during a drag.
  341.      */
  342.     protected class InvocationMouseListener extends MouseAdapter {
  343.         public void mousePressed( MouseEvent e ) {
  344.             Rectangle r;
  345.  
  346.             if ( !SwingUtilities.isLeftMouseButton(e) )
  347.                 return;
  348.  
  349.             if ( !comboBox.isEnabled() )
  350.                 return;
  351.  
  352.             delegateFocus( e );
  353.  
  354.             togglePopup();
  355.         }
  356.  
  357.         public void mouseReleased( MouseEvent e ) {
  358.             Component source = (Component)e.getSource();
  359.             Dimension size = source.getSize();
  360.             Rectangle bounds = new Rectangle( 0, 0, size.width - 1, size.height - 1 );
  361.             if ( !bounds.contains( e.getPoint() ) ) {
  362.                 MouseEvent newEvent = convertMouseEvent( e );
  363.                 Point location = newEvent.getPoint();
  364.                 Rectangle r = new Rectangle();
  365.                 list.computeVisibleRect( r );
  366.                 if ( r.contains( location ) ) {
  367.                     updateListBoxSelectionForEvent( newEvent, false );
  368.                     comboBox.setSelectedIndex( list.getSelectedIndex() );
  369.                 }
  370.                 hide();
  371.             }
  372.             hasEntered = false;
  373.             stopAutoScrolling();
  374.         }
  375.     }
  376.  
  377.     /**
  378.      * This listener watches for dragging and updates the current selection in the
  379.      * list if it is dragging over the list.
  380.      */
  381.     protected class InvocationMouseMotionListener extends MouseMotionAdapter {
  382.         public void mouseDragged( MouseEvent e ) {
  383.             if ( isVisible() ) {
  384.                 MouseEvent newEvent = convertMouseEvent( e );
  385.                 Rectangle r = new Rectangle();
  386.                 list.computeVisibleRect( r );
  387.  
  388.                 if ( newEvent.getPoint().y >= r.y && newEvent.getPoint().y <= r.y + r.height - 1 ) {
  389.                     hasEntered = true;
  390.                     if ( isAutoScrolling ) {
  391.                         stopAutoScrolling();
  392.                     }
  393.                     Point location = newEvent.getPoint();
  394.                     if ( r.contains( location ) ) {
  395.                         valueIsAdjusting = true;
  396.                         updateListBoxSelectionForEvent( newEvent, false );
  397.                         valueIsAdjusting = false;
  398.                     }
  399.                 }
  400.                 else {
  401.                     if ( hasEntered ) {
  402.                         int directionToScroll = newEvent.getPoint().y < r.y ? SCROLL_UP : SCROLL_DOWN;
  403.                         if ( isAutoScrolling && scrollDirection != directionToScroll ) {
  404.                             stopAutoScrolling();
  405.                             startAutoScrolling( directionToScroll );
  406.                         }
  407.                         else if ( !isAutoScrolling ) {
  408.                             startAutoScrolling( directionToScroll );
  409.                         }
  410.                     }
  411.                     else {
  412.                         if ( e.getPoint().y < ((Component)e.getSource()).getBounds().y ) {
  413.                             hasEntered = true;
  414.                             startAutoScrolling( SCROLL_UP );
  415.                         }
  416.                     }
  417.                 }
  418.             }
  419.         }
  420.     }
  421.  
  422.     /**
  423.      * This listener watches for the spacebar being pressed and shows/hides the
  424.      * popup accordingly.
  425.      */
  426.     public class InvocationKeyListener extends KeyAdapter {
  427.         public void keyReleased( KeyEvent e ) {
  428.             if ( e.getKeyCode() == KeyEvent.VK_SPACE ) {
  429.                 togglePopup();
  430.             }
  431.         }
  432.     }
  433.  
  434.     /**
  435.      * This listener watches for changes in the list's selection and reports
  436.      * them to the combo box.
  437.      */
  438.     protected class ListSelListener implements ListSelectionListener {
  439.         public void valueChanged( ListSelectionEvent e ) {
  440.             if ( isVisible() && !valueIsAdjusting && !e.getValueIsAdjusting() ) {
  441.                 comboBox.setSelectedIndex( list.getSelectedIndex() );
  442.             }
  443.         }
  444.     }
  445.  
  446.     /**
  447.      * This listener hides the popup when the mouse is released in the list.
  448.      */
  449.     protected class ListMouseListener extends MouseAdapter {
  450.         public void mouseReleased(MouseEvent anEvent) {
  451.             hide();
  452.         }
  453.     }
  454.  
  455.     /**
  456.      * This listener changes the selected item as you move the mouse over the list.
  457.      * The selection change is not committed to the model, this is for user feedback only.
  458.      */
  459.     protected class ListMouseMotionListener extends MouseMotionAdapter {
  460.         public void mouseMoved( MouseEvent anEvent ) {
  461.             Point location = anEvent.getPoint();
  462.             Rectangle r = new Rectangle();
  463.             list.computeVisibleRect( r );
  464.             if ( r.contains( location ) ) {
  465.                 valueIsAdjusting = true;
  466.                 updateListBoxSelectionForEvent( anEvent, false );
  467.                 valueIsAdjusting = false;
  468.             }
  469.         }
  470.     }
  471.  
  472.     /**
  473.      * This listener watches for changes in the JComboBox's selection.  It updates
  474.      * the list accordingly.
  475.      */
  476.     protected class ComboItemListener implements ItemListener {
  477.         public void itemStateChanged( ItemEvent e ) {
  478.             if ( e.getStateChange() == ItemEvent.SELECTED ) {
  479.                 valueIsAdjusting = true;
  480.                 list.setSelectedIndex( comboBox.getSelectedIndex() ); 
  481.                 valueIsAdjusting = false;
  482.                 list.ensureIndexIsVisible( comboBox.getSelectedIndex() );
  483.             }
  484.         }
  485.     }
  486.  
  487.     /**
  488.      * This listener watches for bound property changes in JComboBox.  If the model
  489.      * or the renderer changes, the popup hides itself.
  490.      */
  491.     protected class ComboPropertyChangeListener implements PropertyChangeListener {
  492.         public void propertyChange( PropertyChangeEvent e ) {
  493.             String propertyName = e.getPropertyName();
  494.  
  495.             if ( propertyName.equals("model") ) {
  496.                 list.setModel( comboBox.getModel() );
  497.                 if ( isVisible() ) {
  498.                     hide();
  499.                 }
  500.             }
  501.             else if ( propertyName.equals( "renderer" ) ) {
  502.                 list.setCellRenderer( comboBox.getRenderer() );
  503.                 if ( isVisible() ) {
  504.                     hide();
  505.                 }
  506.             }
  507.         }
  508.     }
  509.  
  510.     //
  511.     // end Event Listenters
  512.     //=================================================================
  513.  
  514.  
  515.     /**
  516.      * Overridden to unconditionally return false.
  517.      */
  518.     public boolean isFocusTraversable() {
  519.         return false;
  520.     }
  521.  
  522.     //===================================================================
  523.     // begin Autoscroll methods
  524.     //
  525.  
  526.     /**
  527.      * Called by BasicComboPopup$InvocationMouseMotionListener to handle auto-
  528.      * scrolling the list.
  529.      */
  530.     protected void startAutoScrolling( int direction ) {
  531.         if ( isAutoScrolling ) {
  532.             autoscrollTimer.stop();
  533.         }
  534.  
  535.         isAutoScrolling = true;
  536.  
  537.         if ( direction == SCROLL_UP ) {
  538.             scrollDirection = SCROLL_UP;
  539.             Point convertedPoint = SwingUtilities.convertPoint( scroller, new Point( 1, 1 ), list );
  540.             int top = list.locationToIndex( convertedPoint );
  541.             valueIsAdjusting = true;
  542.             list.setSelectedIndex( top );
  543.             valueIsAdjusting = false;
  544.  
  545.             AbstractAction timerAction = new AbstractAction() {
  546.                 public void actionPerformed(ActionEvent e) {
  547.                     autoScrollUp();
  548.                 }
  549.                 public boolean isEnabled() {
  550.                     return true;
  551.                 }
  552.             };
  553.  
  554.             autoscrollTimer = new Timer( 100, timerAction );
  555.         }
  556.         else if ( direction == SCROLL_DOWN ) {
  557.             scrollDirection = SCROLL_DOWN;
  558.             Dimension size = scroller.getSize();
  559.             Point convertedPoint = SwingUtilities.convertPoint( scroller,
  560.                                                                 new Point( 1, (size.height - 1) - 2 ),
  561.                                                                 list );
  562.             int bottom = list.locationToIndex( convertedPoint );
  563.             valueIsAdjusting = true;
  564.             list.setSelectedIndex( bottom );
  565.             valueIsAdjusting = false;
  566.  
  567.             AbstractAction timerAction = new AbstractAction() {
  568.                 public void actionPerformed(ActionEvent e) {
  569.                     autoScrollDown();
  570.                 }
  571.                 public boolean isEnabled() {
  572.                     return true;
  573.                 }
  574.             };
  575.  
  576.             autoscrollTimer = new Timer( 100, timerAction );
  577.         }
  578.         autoscrollTimer.start();
  579.     }
  580.  
  581.     protected void stopAutoScrolling() {
  582.         isAutoScrolling = false;
  583.  
  584.         if ( autoscrollTimer != null ) {
  585.             autoscrollTimer.stop();
  586.             autoscrollTimer = null;
  587.         }
  588.     }
  589.  
  590.     protected void autoScrollUp() {
  591.         int index = list.getSelectedIndex();
  592.         if ( index > 0 ) {
  593.             valueIsAdjusting = true;
  594.             list.setSelectedIndex( index - 1 );
  595.             valueIsAdjusting = false;
  596.             list.ensureIndexIsVisible( index - 1 );
  597.         }
  598.     }
  599.  
  600.     protected void autoScrollDown() {
  601.         int index = list.getSelectedIndex();
  602.         int lastItem = list.getModel().getSize() - 1;
  603.         if ( index < lastItem ) {
  604.             valueIsAdjusting = true;
  605.             list.setSelectedIndex( index + 1 );
  606.             valueIsAdjusting = false;
  607.             list.ensureIndexIsVisible( index + 1 );
  608.         }
  609.     }   
  610.  
  611.     //
  612.     // end Autoscroll methods
  613.     //=================================================================
  614.  
  615.  
  616.     //===================================================================
  617.     // begin Utility methods
  618.     //
  619.  
  620.     /**
  621.      * This is is a utility method that helps event handlers figure out where to
  622.      * send the focus when the popup is brought up.  The standard implementation
  623.      * delegates the focus to the editor (if the combo box is editable) or to
  624.      * the JComboBox if it is not editable.
  625.      */
  626.     protected void delegateFocus( MouseEvent e ) {
  627.         if ( comboBox.isEditable() ) {
  628.             comboBox.getEditor().getEditorComponent().requestFocus();
  629.         }
  630.         else {
  631.             comboBox.requestFocus();
  632.         }
  633.     }
  634.  
  635.     /**
  636.      * Makes the popup visible if it is hidden and makes it hidden if it is visible.
  637.      */
  638.     protected void togglePopup() {
  639.         if ( isVisible() ) {
  640.             hide();
  641.         }
  642.         else {
  643.             show();
  644.         }
  645.     }   
  646.  
  647.     protected MouseEvent convertMouseEvent( MouseEvent e ) {
  648.         Point convertedPoint = SwingUtilities.convertPoint( (Component)e.getSource(),
  649.                                                             e.getPoint(), list );
  650.         MouseEvent newEvent = new MouseEvent( (Component)e.getSource(),
  651.                                               e.getID(),
  652.                                               e.getWhen(),
  653.                                               e.getModifiers(),
  654.                                               convertedPoint.x,
  655.                                               convertedPoint.y,
  656.                                               e.getModifiers(),
  657.                                               e.isPopupTrigger() );
  658.         return newEvent;
  659.     }
  660.  
  661.     protected int getPopupHeightForRowCount(int maxRowCount) {
  662.         int currentElementCount = comboBox.getModel().getSize();
  663.  
  664.         if ( currentElementCount > 0 ) {
  665.             Rectangle r = list.getCellBounds(0,0);
  666.  
  667.             if ( maxRowCount < currentElementCount )
  668.                 return (r.height * maxRowCount) + 2;
  669.             else
  670.                 return (r.height * currentElementCount) + 2;
  671.  
  672.         }
  673.         else
  674.             return 100;
  675.     }
  676.  
  677.     protected Rectangle computePopupBounds(int px,int py,int pw,int ph) {
  678.         Rectangle absBounds;
  679.         Rectangle r = new Rectangle(px,py,pw,ph);
  680.         boolean inModalDialog = inModalDialog();
  681.         /** Workaround for modal dialogs. See also JPopupMenu.java **/
  682.         if ( inModalDialog ) {
  683.             Dialog dlg = getDialog();
  684.             Point p;
  685.             if ( dlg instanceof JDialog ) {
  686.                 JRootPane rp = ((JDialog)dlg).getRootPane();
  687.                 p = rp.getLocationOnScreen();
  688.                 absBounds = rp.getBounds();
  689.                 absBounds.x = p.x;
  690.                 absBounds.y = p.y;
  691.             }
  692.             else
  693.                 absBounds = dlg.getBounds();
  694.             p = new Point(absBounds.x,absBounds.y);
  695.             SwingUtilities.convertPointFromScreen(p,comboBox);
  696.             absBounds.x = p.x;
  697.             absBounds.y = p.y;
  698.         }
  699.         else {
  700.             Point p;
  701.             Dimension scrSize = Toolkit.getDefaultToolkit().getScreenSize();
  702.             absBounds = new Rectangle();
  703.             p = new Point(0,0);
  704.             SwingUtilities.convertPointFromScreen(p,comboBox);
  705.             absBounds.x = p.x;
  706.             absBounds.y = p.y;
  707.             absBounds.width = scrSize.width;
  708.             absBounds.height= scrSize.height;
  709.         }
  710.  
  711.         if ( SwingUtilities.isRectangleContainingRectangle(absBounds,r) )
  712.             return r;
  713.         else {
  714.             Rectangle r2      = new Rectangle(0,-r.height,r.width,r.height);
  715.  
  716.             if ( SwingUtilities.isRectangleContainingRectangle(absBounds,r2) )
  717.                 return r2;
  718.  
  719.             if ( inModalDialog ) {
  720.                 SwingUtilities.computeIntersection(absBounds.x,absBounds.y,absBounds.width,absBounds.height,r);
  721.                 SwingUtilities.computeIntersection(absBounds.x,absBounds.y,absBounds.width,absBounds.height,r2);
  722.                 if ( r.height > r2.height )
  723.                     return r;
  724.                 else
  725.                     return r2;
  726.             }
  727.             else
  728.                 return r2;
  729.         }
  730.     }
  731.  
  732.     private Dialog getDialog() {
  733.         Container parent;
  734.         for ( parent = comboBox.getParent() ; parent != null && !(parent instanceof Dialog)
  735.             && !(parent instanceof Window) ; parent = parent.getParent() );
  736.         if ( parent instanceof Dialog )
  737.             return (Dialog) parent;
  738.         else
  739.             return null;
  740.     }
  741.  
  742.     private boolean inModalDialog() {
  743.         return (getDialog() != null);
  744.     }
  745.  
  746.     /**
  747.      * A utility method used by the event listeners.  Given a mouse event, it changes
  748.      * the list selection to the list item below the mouse.
  749.      */
  750.     protected void updateListBoxSelectionForEvent(MouseEvent anEvent,boolean shouldScroll) {
  751.         Point location = anEvent.getPoint();
  752.         if ( list == null )
  753.             return;
  754.         int index = list.locationToIndex(location);
  755.         if ( index == -1 ) {
  756.             if ( location.y < 0 )
  757.                 index = 0;
  758.             else
  759.                 index = comboBox.getModel().getSize() - 1;
  760.         }
  761.         if ( list.getSelectedIndex() != index ) {
  762.             list.setSelectedIndex(index);
  763.             if ( shouldScroll )
  764.                 list.ensureIndexIsVisible(index);
  765.         }
  766.     }
  767.  
  768.     //
  769.     // end Utility methods
  770.     //=================================================================
  771. }
  772.  
  773.  
  774.